package com.nado.extensions.data;

import static com.nado.extensions.data.DataHandlerInfo.*;

import com.nado.extensions.util.SpringObjectFactory;

import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;


/**
 * {@link DataHandlerInfo}를 이용하여 환경 정보 모델을 구성하는 팩토리 클래스.
 * <p/>
 * DataHandlerInfo에 지정된 환경 데이터 리소스는 {@link com.nado.extensions.util.ResourceLoader}를 통해 얻어지며,
 * 성공적으로 파싱 과정이 끝난 경우 최상위 노드에 대한 {@link DataHandler} 객체를 제공하게 된다.
 *
 * @see DataHandler
 * @see com.nado.extensions.util.ResourceLoader
 */
public class DataHandlerFactory {

	static final String DEFAULT_LOCATION = "data.xml";
	static final Log log = LogFactory.getLog(DataHandlerFactory.class);

	/**
	 * DataHandler 참조 객체
	 */
	private static Map<String, DataHandler> HANDLERS = new ConcurrentHashMap<String, DataHandler>();

	/**
	 * DataHandler 참조 객체를 생성하여 등록한다.
	 */
	public static DataHandler init(DataHandlerInfo handlerInfo) {
		DataHandler handler = create(handlerInfo.getType(), handlerInfo.getLocation());
		if (handlerInfo.isCached()) {
			handler = new DataHandlerProxy(handler);
		}
		((AbstractDataHandler) handler).setHandlerName(handlerInfo.getName());
		HANDLERS.put(handlerInfo.getName(), handler);

		return handler;
	}

	public static DataHandler create(String location) {
		return create(TYPE_JAXP, location);
	}

	/**
	 * DataHandler 참조 객체를 생성한다.
	 */
	public static DataHandler create(String handlerType, String location) {
		if (StringUtils.isEmpty(location)) {
			location = DEFAULT_LOCATION;
		}
		DataHandler handler;
		if (TYPE_DOM4J.equalsIgnoreCase(handlerType)) {
			handler = new Dom4jXmlDataHandler(location);
		} else if (TYPE_JAXP.equalsIgnoreCase(handlerType)) {
			handler = new JaxpXmlDataHandler(location);
		} else {
			handler = SpringObjectFactory.getInstance().getObject(handlerType, new Object[]{location});
		}
		return handler;
	}

	public static DataHandler wrap(org.dom4j.Node root) {
		return new Dom4jXmlDataHandler(root);
	}

	public static DataHandler wrap(org.w3c.dom.Node root) {
		return new JaxpXmlDataHandler(root);
	}

	/**
	 * DataHandler 참조 객체를 이용해서 초기화한다.
	 */
	public static DataHandler init(DataHandler dataHandler) {
		return init(DataHandlerInfo.DEFAULT_NAME, dataHandler);
	}

	/**
	 * DataHandler 참조 객체를 이용해서 초기화한다.
	 */
	public static DataHandler init(String name, DataHandler dataHandler) {
		return HANDLERS.put(name, dataHandler);
	}

	/**
	 * default DataHandler 참조 객체를 반환한다.
	 *
	 * @throws IllegalStateException {@link DataHandler}가 생성되지 않은 상태에서 호출될 경우.
	 */
	public static DataHandler getDataHandler() throws IllegalStateException {
		return getDataHandler(DataHandlerInfo.DEFAULT_NAME);
	}

	/**
	 * DataHandler 참조 객체를 반환한다.
	 *
	 * @throws IllegalStateException {@link DataHandler}가 생성되지 않은 상태에서 호출될 경우.
	 */
	public static DataHandler getDataHandler(String name) throws IllegalStateException {
		DataHandler dataHandler = HANDLERS.get(name);
		if (dataHandler == null) {
			throw new IllegalStateException("data handler '" + name + "' is not initialized");
		}
		return dataHandler;
	}

	public static void destroy(String name) {
		DataHandler dataHandler = HANDLERS.remove(name);
		if (dataHandler != null) {
			dataHandler.destroy();
		}
	}
}
